// Yadan @Thingslink 
// MQTT 
// esp8266
// Author Zack
// Version V0.5.1.210417

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <EEPROM.h>        //EEPROM
#include "SimpleDHT.h"
#include <Ticker.h>

Ticker flipper;

//传感器有关
int pinDHT11 = D3;                  //IO0
SimpleDHT11 dht11(pinDHT11);
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
byte isReadTH=0;
//Wifi 有关
const char* ssid = "1234567";            // WIFI名和密码
const char* password = "23456789";
ESP8266WebServer server(80);       // 以下 String html = "  "; 是网页部分
//MQTT 有关
WiFiClient espClient;                                                         // 定义wifiClient实例
PubSubClient client(espClient);                                         // 定义PubSubClient的实例
char wifiConnLoopi = 0;


const char* mqtt_server = "sip.iot.open.thingslink.com"; // 使用HIVEMQ 的信息中转服务
const char* client_id = "";                   // 标识当前设备的客户端编号
const char* client_pwd="";
const char* TOPIC = "/";                     // 订阅信息主题
String cid=client_id;

struct config_type
{
  char stassid[32];
  char stapsw[64];
  char clientid[20];
  char clientpwd[20];
};
config_type config;


long lastMsg = 0;                                                               // 记录上一次发送信息的时长

String prepareHtmlPage(){
  String htmlPage =
      String("") +
            "<!DOCTYPE html> <html>\n"+
            "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"+
            "<title>Ya Dan Device</title>\n"+
            "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"+
            "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n"+
            ".button {display: block;width: 120px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n"+
            ".button-on {background-color: #5AD2FF;}\n"+
            ".button-on:active {background-color: #DE341E;}\n"+
           
            ".button-off {background-color: #34495e;}\n"+
            ".button-off:active {background-color: #DE341E;}\n"+
            "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n"+
            "</style>\n"+
            "</head>\n"+
            "<body>\n"+
            "<h1>Ya Dan Device </h1>\n"+
            "<h3>Server Connected: "+String(client.connected()?"OK":"NO")+"----Light(PIN2): "+String(digitalRead(2)==0?"ON":"OFF")+"</h3>\n"+
            "<h3>Temperature: "+String(temperature)+"----Humidity: "+String(humidity)+"</h3>\n"+
            
            "<a id='F1' href='./pin?light1=1'><input class='button button-on' type='submit' value='ON' ></a><br>"+
            "<a id='F2' href='./pin?light1=0'><input class='button button-off' type='submit' value='OFF' ></a><br>"+
"<a id='F3' href='./setting'>Go to setting device.</a><br>"+

            "</html>" +
            "\r\n";
  return htmlPage;
}
String settingHtmlPage(){
      String settingHtml="<html><head><title>Ya Dan Device Setting</title><meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0'><script>function submit(){var ssid=document.getElementById('SSID').value;var password=document.getElementById('password').value;var clientid=document.getElementById('clientid').value;var clientpwd=document.getElementById('clientpwd').value;window.open('./setting/do?ssid='+ssid+'&clientid='+clientid+'&clientpwd='+clientpwd+'&password='+password,'_top');}</script><style>input{height:30px;margin-left:10px;}body{margin:auto;text-align:center;font:18px;line-height:40px;}button{width:35%;}</style></head><body><div ><br/><p style='font-size: 30px;color:blue'>Ya Dan Device Setting</p><br>SSID:<input id='SSID' type='text' placeholder='Wifi Name' value='"+String(config.stassid)+"'/><br>Password:<input id='password'  type='password' placeholder=' Key in password' value='"+String(config.stapsw)+"'/><br>ClientID:<input id='clientid' type='text' placeholder='client id'  value='"+String(config.clientid)+"'/><br>ClientPWD:<input id='clientpwd' type='text' placeholder='client password'  value='"+String(config.clientpwd)+"'/><br><button onclick='submit()'> submit</button> <br><a href='/'>Go To Control Page!</a></div></body></html>";
      return settingHtml; 
  }
/*
   保存参数到EEPROM
*/
void saveConfig()
{
  Serial.println("Save config!");
  Serial.print("stassid:");
  Serial.println(config.stassid);
  Serial.print("stapsw:");
  Serial.println(config.stapsw);
  Serial.print("client id:");
  Serial.println(config.clientid);
  Serial.print("client password:");
  Serial.println(config.clientpwd);
  
  EEPROM.begin(1024);
  uint8_t *p = (uint8_t*)(&config);
  for (int i = 0; i < sizeof(config); i++)
  {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
}

/*
   从EEPROM加载参数
*/
void loadConfig()
{
  EEPROM.begin(1024);
  uint8_t *p = (uint8_t*)(&config);
  for (int i = 0; i < sizeof(config); i++)
  {
    *(p + i) = EEPROM.read(i);
  }
  EEPROM.commit();
  Serial.println("-----Read config-----");
  Serial.print("stassid:");
  Serial.println(config.stassid);
  Serial.print("stapsw:");
  Serial.println(config.stapsw);
  Serial.print("client id:");
  Serial.println(config.clientid);
  Serial.print("client password:");
  Serial.println(config.clientpwd);
}




void pin() {
  if (server.arg("light1") == "1") {
    digitalWrite(2, LOW);   // 2端口
    Serial.println("2-0");
    server.send(200, "text/html", prepareHtmlPage());
  }
  else if (server.arg("light1") == "0") {
    digitalWrite(2, HIGH);
    Serial.println("2-1");
    server.send(200, "text/html", prepareHtmlPage());
  }
}

void setting(){
  Serial.println(WiFi.status());
  Serial.println(WL_CONNECTED);
  
        ssid=server.arg("ssid").c_str(); 
       password=server.arg("password").c_str(); 

       client_id=server.arg("clientid").c_str(); 
       client_pwd=server.arg("clientpwd").c_str(); 

      strcpy(config.stassid, ssid);
      strcpy(config.stapsw, password);
      strcpy(config.clientid, client_id);
      strcpy(config.clientpwd, client_pwd);
      saveConfig();
      
      
      //Serial.printf("\nConnecting to %s\nIP Address : %s\n", ssid, WiFi.localIP().toString().c_str());
    
      server.send(200, "text/html", settingHtmlPage());
      delay(200);
   WiFi.disconnect();
}

void handleRoot() {
  server.send(200, "text/html", prepareHtmlPage());
}

void settingPage(){ 
  server.send(200, "text/html", settingHtmlPage());
}


void setup(void) {                  //定义端口号16，4，5，2，14，12，13，15，0
  pinMode(2, OUTPUT);
  Serial.begin(115200);

  flipper.attach(2, getTempAndHum);
  
  loadConfig(); //读取账号和密码
  WiFi.mode(WIFI_AP_STA);  
  //WIFI模块的STA模式和AP模式有什么区别:AP是接入点，可以让用户接入。STA--Station无线终端，不接受无线接入，可以连接到无线AP，无线网卡工作在STA下

// 设置内网
  IPAddress softLocal(192,168,128,1);   // 1 设置内网WIFI IP地址
  IPAddress softGateway(192,168,128,1);
  IPAddress softSubnet(255,255,255,0);
  WiFi.softAPConfig(softLocal, softGateway, softSubnet);
   
  String apName = ("Yadan_"+(String)ESP.getChipId());  // 2 设置WIFI名称
  const char *softAPName = apName.c_str();
  WiFi.softAP(softAPName, "adminadmin");      // 3创建wifi  名称 +密码 adminadmin
   
  IPAddress myIP = WiFi.softAPIP();  // 4输出创建的WIFI IP地址
  Serial.print("AP IP address: ");     
  Serial.println(myIP);
   
  Serial.print("softAPName: ");  // 5输出WIFI 名称
  Serial.println(apName);

  client.setServer(mqtt_server, 5883);                              //设定MQTT服务器与使用的端口，1883是默认的MQTT端口
  client.setCallback(callback);                                          //设定回调方式，当ESP8266收到订阅消息时会调用此方法

  server.on("/", handleRoot);
  server.on("/setting", settingPage);
  server.on("/pin", HTTP_GET, pin);
  server.on("/setting/do", HTTP_GET, setting);
  server.begin();
}


void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);   // 打印主题信息
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]); // 打印主题内容
  }
  Serial.println();
  DynamicJsonDocument doc(1024);
  doc.clear();
  String output="";
  deserializeJson(doc, payload);
    serializeJson(doc, output);
  Serial.println(output);
  JsonObject obj = doc.as<JsonObject>();

  const char* xx=obj["sensorID"];
      // Make the decision to turn off or on the LED
      if (strcmp(xx, "1") == 0) {
         doc["value"]=(int)temperature;
      }
      else if(strcmp(xx, "2") == 0) {
        doc["value"]=(int)humidity;
      }
  output="";
  serializeJson(doc, output);
  Serial.println(output);
  cid=config.clientid;
  client.publish(("d/"+cid+"/route").c_str(),output.c_str());

  obj.clear();
  doc.clear();

}

void reconnect() {
  if (!client.connected()) {
    //while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(config.clientid,config.clientid,config.clientpwd)) {
      Serial.println("connected");
      // 连接成功时订阅主题
      cid=config.clientid;
      client.subscribe(("d/"+cid+"/getroute").c_str());
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void loop(void) {
  server.handleClient();

  if(WiFi.status() == WL_CONNECTED){
     
      if (!client.connected()) {
          reconnect();
        }
      client.loop();
      }
  else{
      if(wifiConnLoopi==0) WiFi.begin(config.stassid, config.stapsw);   // 以下是串口监视器显示内容。
    Serial.println(WiFi.status());
    
    if (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print("..");
      wifiConnLoopi = wifiConnLoopi + 1;
      if (wifiConnLoopi > 10) {
        wifiConnLoopi=0;
       
      }
    }else{
    Serial.printf("\nConnecting to %s\nIP Address : %s\n", ssid, WiFi.localIP().toString().c_str());
    }
      
    }
      
  if(isReadTH==0){
    if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
        Serial.print("Read DHT11 failed, err="); Serial.println(err);delay(1000);
        return;
      }
      isReadTH=1;
  }
}
void getTempAndHum()
{

  Serial.print((int)temperature); Serial.print(" *C, "); 
  Serial.print((int)humidity); Serial.println(" H");
  isReadTH=0;
  }
